#ifndef __EPSILON__VECTOR3_H__
#define __EPSILON__VECTOR3_H__

#include "math.h"
#include "debug.h" // Operator [] throws a few assertions...

namespace Epsilon
{

template <class Real>
class Vector3
{
public:
    // Construction
    Vector3 (void);  // Uninitialized
    Vector3 (Real fX, Real fY, Real fZ);
    Vector3 (const Vector3& rkV);

    // Coordinate Access
    operator const Real* (void) const;
    operator       Real* (void);
    Real  operator[] (int i) const;
    Real& operator[] (int i);
    Real  X (void) const;
    Real& X (void);
    Real  Y (void) const;
    Real& Y (void);
    Real  Z (void) const;
    Real& Z (void);

    // Assignment
    Vector3& operator= (const Vector3& rkV);

    // Comparison
    bool operator== (const Vector3& rkV) const;
    bool operator!= (const Vector3& rkV) const;
    bool operator<  (const Vector3& rkV) const;
    bool operator<= (const Vector3& rkV) const;
    bool operator>  (const Vector3& rkV) const;
    bool operator>= (const Vector3& rkV) const;

    // Arithmetic operations
    Vector3 operator+ (const Vector3& rkV) const;
    Vector3 operator- (const Vector3& rkV) const;
    Vector3 operator* (Real fScalar) const;
    Vector3 operator/ (Real fScalar) const;
    Vector3 operator- (void) const;

    // Arithmetic updates
    Vector3& operator+= (const Vector3& rkV);
    Vector3& operator-= (const Vector3& rkV);
    Vector3& operator*= (Real fScalar);
    Vector3& operator/= (Real fScalar);

    // Vector operations
    Real Length        (void) const;
    Real SquaredLength (void) const;
    Real Dot           (const Vector3& rkV) const;
    Real Normalize     (void);

    // The cross products are computed using the right-handed rule.  Be aware
    // that some graphics APIs use a left-handed rule.  If you have to compute
    // a cross product with these functions and send the result to the API
    // that expects left-handed, you will need to change sign on the vector
    // (replace each component value c by -c).
    Vector3 Cross     (const Vector3& rkV) const;
    Vector3 UnitCross (const Vector3& rkV) const;

    // Compute the barycentric coordinates of the point with respect to the
    // tetrahedron <V0,V1,V2,V3>, P = b0*V0 + b1*V1 + b2*V2 + b3*V3, where
    // b0 + b1 + b2 + b3 = 1.
    void GetBarycentrics (const Vector3<Real>& rkV0,
                          const Vector3<Real>& rkV1, const Vector3<Real>& rkV2,
                          const Vector3<Real>& rkV3, Real afBary [4]) const;

    // Gram-Schmidt orthonormalization.  Take linearly independent vectors
    // U, V, and W and compute an orthonormal set (unit length, mutually
    // perpendicular).
    static void Orthonormalize (Vector3& rkU, Vector3& rkV, Vector3& rkW);
    static void Orthonormalize (Vector3* akV);

    // Input W must be initialized to a nonzero vector, output is {U,V,W},
    // an orthonormal basis.  A hint is provided about whether or not W
    // is already unit length.
    static void GenerateOrthonormalBasis (Vector3& rkU, Vector3& rkV,
                                          Vector3& rkW, bool bUnitLengthW);

    // Special vectors
    EPSILON_API static const Vector3 ZERO;
    EPSILON_API static const Vector3 UNIT_X;
    EPSILON_API static const Vector3 UNIT_Y;
    EPSILON_API static const Vector3 UNIT_Z;

private:
    // Support for comparisons
    int CompareArrays (const Vector3& rkV) const;

    Real m_afTuple [3];
};

template <class Real>
Vector3<Real> operator* (Real fScalar, const Vector3<Real>& rkV);

#include "vector3.inl"

typedef Vector3<float> Vector3f;
typedef Vector3<double> Vector3d;

}

#endif /* __EPSILON__VECTOR3_H__ */
